home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / iceweasel / modules / PlacesUIUtils.jsm < prev    next >
Encoding:
Text File  |  2013-01-09  |  51.3 KB  |  1,460 lines

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is the Places Command Controller.
  16.  *
  17.  * The Initial Developer of the Original Code is Google Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Ben Goodger <beng@google.com>
  23.  *   Myk Melez <myk@mozilla.org>
  24.  *   Asaf Romano <mano@mozilla.com>
  25.  *   Sungjoon Steve Won <stevewon@gmail.com>
  26.  *   Dietrich Ayala <dietrich@mozilla.com>
  27.  *   Marco Bonardo <mak77@bonardo.net>
  28.  *
  29.  * Alternatively, the contents of this file may be used under the terms of
  30.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  31.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32.  * in which case the provisions of the GPL or the LGPL are applicable instead
  33.  * of those above. If you wish to allow use of your version of this file only
  34.  * under the terms of either the GPL or the LGPL, and not to allow others to
  35.  * use your version of this file under the terms of the MPL, indicate your
  36.  * decision by deleting the provisions above and replace them with the notice
  37.  * and other provisions required by the GPL or the LGPL. If you do not delete
  38.  * the provisions above, a recipient may use your version of this file under
  39.  * the terms of any one of the MPL, the GPL or the LGPL.
  40.  *
  41.  * ***** END LICENSE BLOCK ***** */
  42.  
  43. var EXPORTED_SYMBOLS = ["PlacesUIUtils"];
  44.  
  45. var Ci = Components.interfaces;
  46. var Cc = Components.classes;
  47. var Cr = Components.results;
  48. var Cu = Components.utils;
  49.  
  50. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  51. Cu.import("resource://gre/modules/Services.jsm");
  52.  
  53. XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
  54.   Cu.import("resource://gre/modules/PlacesUtils.jsm");
  55.   return PlacesUtils;
  56. });
  57.  
  58. var PlacesUIUtils = {
  59.   ORGANIZER_LEFTPANE_VERSION: 7,
  60.   ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
  61.   ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
  62.  
  63.   LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
  64.   DESCRIPTION_ANNO: "bookmarkProperties/description",
  65.  
  66.   TYPE_TAB_DROP: "application/x-moz-tabbrowser-tab",
  67.  
  68.   /**
  69.    * Makes a URI from a spec, and do fixup
  70.    * @param   aSpec
  71.    *          The string spec of the URI
  72.    * @returns A URI object for the spec.
  73.    */
  74.   createFixedURI: function PUIU_createFixedURI(aSpec) {
  75.     return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
  76.   },
  77.  
  78.   getFormattedString: function PUIU_getFormattedString(key, params) {
  79.     return bundle.formatStringFromName(key, params, params.length);
  80.   },
  81.  
  82.   getString: function PUIU_getString(key) {
  83.     return bundle.GetStringFromName(key);
  84.   },
  85.  
  86.   get _copyableAnnotations() [
  87.     this.DESCRIPTION_ANNO,
  88.     this.LOAD_IN_SIDEBAR_ANNO,
  89.     PlacesUtils.POST_DATA_ANNO,
  90.     PlacesUtils.READ_ONLY_ANNO,
  91.   ],
  92.  
  93.   /**
  94.    * Get a transaction for copying a uri item (either a bookmark or a history
  95.    * entry) from one container to another.
  96.    *
  97.    * @param   aData
  98.    *          JSON object of dropped or pasted item properties
  99.    * @param   aContainer
  100.    *          The container being copied into
  101.    * @param   aIndex
  102.    *          The index within the container the item is copied to
  103.    * @return A nsITransaction object that performs the copy.
  104.    *
  105.    * @note Since a copy creates a completely new item, only some internal
  106.    *       annotations are synced from the old one.
  107.    * @see this._copyableAnnotations for the list of copyable annotations.
  108.    */
  109.   _getURIItemCopyTransaction:
  110.   function PUIU__getURIItemCopyTransaction(aData, aContainer, aIndex)
  111.   {
  112.     let transactions = [];
  113.     if (aData.dateAdded) {
  114.       transactions.push(
  115.         new PlacesEditItemDateAddedTransaction(null, aData.dateAdded)
  116.       );
  117.     }
  118.     if (aData.lastModified) {
  119.       transactions.push(
  120.         new PlacesEditItemLastModifiedTransaction(null, aData.lastModified)
  121.       );
  122.     }
  123.  
  124.     let keyword = aData.keyword || null;
  125.     let annos = [];
  126.     if (aData.annos) {
  127.       annos = aData.annos.filter(function (aAnno) {
  128.         return this._copyableAnnotations.indexOf(aAnno.name) != -1;
  129.       }, this);
  130.     }
  131.  
  132.     return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(aData.uri),
  133.                                                aContainer, aIndex, aData.title,
  134.                                                keyword, annos, transactions);
  135.   },
  136.  
  137.   /**
  138.    * Gets a transaction for copying (recursively nesting to include children)
  139.    * a folder (or container) and its contents from one folder to another.
  140.    *
  141.    * @param   aData
  142.    *          Unwrapped dropped folder data - Obj containing folder and children
  143.    * @param   aContainer
  144.    *          The container we are copying into
  145.    * @param   aIndex
  146.    *          The index in the destination container to insert the new items
  147.    * @return A nsITransaction object that will perform the copy.
  148.    *
  149.    * @note Since a copy creates a completely new item, only some internal
  150.    *       annotations are synced from the old one.
  151.    * @see this._copyableAnnotations for the list of copyable annotations.
  152.    */
  153.   _getFolderCopyTransaction:
  154.   function PUIU__getFolderCopyTransaction(aData, aContainer, aIndex)
  155.   {
  156.     function getChildItemsTransactions(aChildren)
  157.     {
  158.       let transactions = [];
  159.       let index = aIndex;
  160.       aChildren.forEach(function (node, i) {
  161.         // Make sure that items are given the correct index, this will be
  162.         // passed by the transaction manager to the backend for the insertion.
  163.         // Insertion behaves differently for DEFAULT_INDEX (append).
  164.         if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX) {
  165.           index = i;
  166.         }
  167.  
  168.         if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
  169.           if (node.livemark && node.annos) {
  170.             transactions.push(
  171.               PlacesUIUtils._getLivemarkCopyTransaction(node, aContainer, index)
  172.             );
  173.           }
  174.           else {
  175.             transactions.push(
  176.               PlacesUIUtils._getFolderCopyTransaction(node, aContainer, index)
  177.             );
  178.           }
  179.         }
  180.         else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
  181.           transactions.push(new PlacesCreateSeparatorTransaction(-1, index));
  182.         }
  183.         else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE) {
  184.           transactions.push(
  185.             PlacesUIUtils._getURIItemCopyTransaction(node, -1, index)
  186.           );
  187.         }
  188.         else {
  189.           throw new Error("Unexpected item under a bookmarks folder");
  190.         }
  191.       });
  192.       return transactions;
  193.     }
  194.  
  195.     if (aContainer == PlacesUtils.tagsFolderId) { // Copying a tag folder.
  196.       let transactions = [];
  197.       if (aData.children) {
  198.         aData.children.forEach(function(aChild) {
  199.           transactions.push(
  200.             new PlacesTagURITransaction(PlacesUtils._uri(aChild.uri),
  201.                                         [aData.title])
  202.           );
  203.         });
  204.       }
  205.       return new PlacesAggregatedTransaction("addTags", transactions);
  206.     }
  207.  
  208.     if (aData.livemark && aData.annos) { // Copying a livemark.
  209.       return this._getLivemarkCopyTransaction(aData, aContainer, aIndex);
  210.     }
  211.  
  212.     let transactions = getChildItemsTransactions(aData.children);
  213.     if (aData.dateAdded) {
  214.       transactions.push(
  215.         new PlacesEditItemDateAddedTransaction(null, aData.dateAdded)
  216.       );
  217.     }
  218.     if (aData.lastModified) {
  219.       transactions.push(
  220.         new PlacesEditItemLastModifiedTransaction(null, aData.lastModified)
  221.       );
  222.     }
  223.  
  224.     let annos = [];
  225.     if (aData.annos) {
  226.       annos = aData.annos.filter(function (aAnno) {
  227.         return this._copyableAnnotations.indexOf(aAnno.name) != -1;
  228.       }, this);
  229.     }
  230.  
  231.     return new PlacesCreateFolderTransaction(aData.title, aContainer, aIndex,
  232.                                              annos, transactions);
  233.   },
  234.  
  235.   /**
  236.    * Gets a transaction for copying a live bookmark item from one container to
  237.    * another.
  238.    *
  239.    * @param   aData
  240.    *          Unwrapped live bookmarkmark data
  241.    * @param   aContainer
  242.    *          The container we are copying into
  243.    * @param   aIndex
  244.    *          The index in the destination container to insert the new items
  245.    * @return A nsITransaction object that will perform the copy.
  246.    *
  247.    * @note Since a copy creates a completely new item, only some internal
  248.    *       annotations are synced from the old one.
  249.    * @see this._copyableAnnotations for the list of copyable annotations.
  250.    */
  251.   _getLivemarkCopyTransaction:
  252.   function PUIU__getLivemarkCopyTransaction(aData, aContainer, aIndex)
  253.   {
  254.     if (!aData.livemark || !aData.annos) {
  255.       throw new Error("node is not a livemark");
  256.     }
  257.  
  258.     let feedURI, siteURI;
  259.     let annos = [];
  260.     if (aData.annos) {
  261.       annos = aData.annos.filter(function (aAnno) {
  262.         if (aAnno.name == PlacesUtils.LMANNO_FEEDURI) {
  263.           feedURI = PlacesUtils._uri(aAnno.value);
  264.         }
  265.         else if (aAnno.name == PlacesUtils.LMANNO_SITEURI) {
  266.           siteURI = PlacesUtils._uri(aAnno.value);
  267.         }
  268.         return this._copyableAnnotations.indexOf(aAnno.name) != -1
  269.       }, this);
  270.     }
  271.  
  272.     return new PlacesCreateLivemarkTransaction(feedURI, siteURI, aData.title,
  273.                                                aContainer, aIndex, annos);
  274.   },
  275.  
  276.   /**
  277.    * Constructs a Transaction for the drop or paste of a blob of data into
  278.    * a container.
  279.    * @param   data
  280.    *          The unwrapped data blob of dropped or pasted data.
  281.    * @param   type
  282.    *          The content type of the data
  283.    * @param   container
  284.    *          The container the data was dropped or pasted into
  285.    * @param   index
  286.    *          The index within the container the item was dropped or pasted at
  287.    * @param   copy
  288.    *          The drag action was copy, so don't move folders or links.
  289.    * @returns An object implementing nsITransaction that can perform
  290.    *          the move/insert.
  291.    */
  292.   makeTransaction:
  293.   function PUIU_makeTransaction(data, type, container, index, copy)
  294.   {
  295.     switch (data.type) {
  296.       case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER:
  297.         if (copy) {
  298.           return this._getFolderCopyTransaction(data, container, index);
  299.         }
  300.  
  301.         // Otherwise move the item.
  302.         return new PlacesMoveItemTransaction(data.id, container, index);
  303.         break;
  304.       case PlacesUtils.TYPE_X_MOZ_PLACE:
  305.         if (copy || data.id == -1) { // Id is -1 if the place is not bookmarked.
  306.           return this._getURIItemCopyTransaction(data, container, index);
  307.         }
  308.  
  309.         // Otherwise move the item.
  310.         return new PlacesMoveItemTransaction(data.id, container, index);
  311.         break;
  312.       case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR:
  313.         if (copy) {
  314.           // There is no data in a separator, so copying it just amounts to
  315.           // inserting a new separator.
  316.           return new PlacesCreateSeparatorTransaction(container, index);
  317.         }
  318.  
  319.         // Otherwise move the item.
  320.         return new PlacesMoveItemTransaction(data.id, container, index);
  321.         break;
  322.       default:
  323.         if (type == PlacesUtils.TYPE_X_MOZ_URL ||
  324.             type == PlacesUtils.TYPE_UNICODE ||
  325.             type == this.TYPE_TAB_DROP) {
  326.           let title = type != PlacesUtils.TYPE_UNICODE ? data.title
  327.                                                        : data.uri;
  328.           return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(data.uri),
  329.                                                      container, index, title);
  330.         }
  331.     }
  332.     return null;
  333.   },
  334.  
  335.   _reportDeprecatedAddBookmarkMethod:
  336.   function PUIU__reportDeprecatedAddBookmarkMethod() {
  337.     // Removes "PUIU_".
  338.     let oldFuncName = arguments.callee.caller.name.slice(5);
  339.     Cu.reportError(oldFuncName + " is deprecated and will be removed in a " +
  340.                    "future release. Use showBookmarkDialog instead.");
  341.   },
  342.  
  343.   /**
  344.    * This is here for compatibility reasons, use ShowBookmarkDialog instead.
  345.    */
  346.   showAddBookmarkUI: function PUIU_showAddBookmarkUI(
  347.     aURI, aTitle, aDescription, aDefaultInsertionPoint, aShowPicker,
  348.     aLoadInSidebar, aKeyword, aPostData, aCharSet) {
  349.     this._reportDeprecatedAddBookmarkMethod();
  350.  
  351.     var info = {
  352.       action: "add",
  353.       type: "bookmark"
  354.     };
  355.  
  356.     if (aURI)
  357.       info.uri = aURI;
  358.  
  359.     // allow default empty title
  360.     if (typeof(aTitle) == "string")
  361.       info.title = aTitle;
  362.  
  363.     if (aDescription)
  364.       info.description = aDescription;
  365.  
  366.     if (aDefaultInsertionPoint) {
  367.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  368.       if (!aShowPicker)
  369.         info.hiddenRows = ["folderPicker"];
  370.     }
  371.  
  372.     if (aLoadInSidebar)
  373.       info.loadBookmarkInSidebar = true;
  374.  
  375.     if (typeof(aKeyword) == "string") {
  376.       info.keyword = aKeyword;
  377.       if (typeof(aPostData) == "string")
  378.         info.postData = aPostData;
  379.       if (typeof(aCharSet) == "string")
  380.         info.charSet = aCharSet;
  381.     }
  382.  
  383.     return this.showBookmarkDialog(info);
  384.   },
  385.  
  386.   /**
  387.    * This is here for compatibility reasons, use ShowBookmarkDialog instead.
  388.    */
  389.   showMinimalAddBookmarkUI:
  390.   function PUIU_showMinimalAddBookmarkUI(
  391.     aURI, aTitle, aDescription, aDefaultInsertionPoint, aShowPicker,
  392.     aLoadInSidebar, aKeyword, aPostData, aCharSet) {
  393.     this._reportDeprecatedAddBookmarkMethod();
  394.  
  395.     var info = {
  396.       action: "add",
  397.       type: "bookmark",
  398.       hiddenRows: ["description"]
  399.     };
  400.     if (aURI)
  401.       info.uri = aURI;
  402.  
  403.     // allow default empty title
  404.     if (typeof(aTitle) == "string")
  405.       info.title = aTitle;
  406.  
  407.     if (aDescription)
  408.       info.description = aDescription;
  409.  
  410.     if (aDefaultInsertionPoint) {
  411.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  412.       if (!aShowPicker)
  413.         info.hiddenRows.push("folderPicker");
  414.     }
  415.  
  416.     if (aLoadInSidebar)
  417.       info.loadBookmarkInSidebar = true;
  418.     else
  419.       info.hiddenRows = info.hiddenRows.concat(["location", "loadInSidebar"]);
  420.  
  421.     if (typeof(aKeyword) == "string") {
  422.       info.keyword = aKeyword;
  423.       // Hide the Tags field if we are adding a keyword.
  424.       info.hiddenRows.push("tags");
  425.       // Keyword related params.
  426.       if (typeof(aPostData) == "string")
  427.         info.postData = aPostData;
  428.       if (typeof(aCharSet) == "string")
  429.         info.charSet = aCharSet;
  430.     }
  431.     else
  432.       info.hiddenRows.push("keyword");
  433.  
  434.     return this.showBookmarkDialog(info, undefined, true);
  435.   },
  436.  
  437.   /**
  438.    * This is here for compatibility reasons, use ShowBookmarkDialog instead.
  439.    */
  440.   showAddLivemarkUI: function PUIU_showAddLivemarkURI(aFeedURI,
  441.                                                       aSiteURI,
  442.                                                       aTitle,
  443.                                                       aDescription,
  444.                                                       aDefaultInsertionPoint,
  445.                                                       aShowPicker) {
  446.     this._reportDeprecatedAddBookmarkMethod();
  447.  
  448.     var info = {
  449.       action: "add",
  450.       type: "livemark"
  451.     };
  452.  
  453.     if (aFeedURI)
  454.       info.feedURI = aFeedURI;
  455.     if (aSiteURI)
  456.       info.siteURI = aSiteURI;
  457.  
  458.     // allow default empty title
  459.     if (typeof(aTitle) == "string")
  460.       info.title = aTitle;
  461.  
  462.     if (aDescription)
  463.       info.description = aDescription;
  464.  
  465.     if (aDefaultInsertionPoint) {
  466.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  467.       if (!aShowPicker)
  468.         info.hiddenRows = ["folderPicker"];
  469.     }
  470.     return this.showBookmarkDialog(info);
  471.   },
  472.  
  473.   /**
  474.    * This is here for compatibility reasons, use ShowBookmarkDialog instead.
  475.    */
  476.   showMinimalAddLivemarkUI:
  477.   function PUIU_showMinimalAddLivemarkURI(
  478.     aFeedURI, aSiteURI, aTitle, aDescription, aDefaultInsertionPoint,
  479.     aShowPicker) {
  480.  
  481.     this._reportDeprecatedAddBookmarkMethod();
  482.  
  483.     var info = {
  484.       action: "add",
  485.       type: "livemark",
  486.       hiddenRows: ["feedLocation", "siteLocation", "description"]
  487.     };
  488.  
  489.     if (aFeedURI)
  490.       info.feedURI = aFeedURI;
  491.     if (aSiteURI)
  492.       info.siteURI = aSiteURI;
  493.  
  494.     // allow default empty title
  495.     if (typeof(aTitle) == "string")
  496.       info.title = aTitle;
  497.  
  498.     if (aDescription)
  499.       info.description = aDescription;
  500.  
  501.     if (aDefaultInsertionPoint) {
  502.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  503.       if (!aShowPicker)
  504.         info.hiddenRows.push("folderPicker");
  505.     }
  506.     return this.showBookmarkDialog(info, undefined, true);
  507.   },
  508.  
  509.   /**
  510.    * This is here for compatibility reasons, use ShowBookmarkDialog instead.
  511.    */
  512.   showMinimalAddMultiBookmarkUI: function PUIU_showAddMultiBookmarkUI(aURIList) {
  513.     this._reportDeprecatedAddBookmarkMethod();
  514.  
  515.     if (aURIList.length == 0)
  516.       throw("showAddMultiBookmarkUI expects a list of nsIURI objects");
  517.     var info = {
  518.       action: "add",
  519.       type: "folder",
  520.       hiddenRows: ["description"],
  521.       URIList: aURIList
  522.     };
  523.     return this.showBookmarkDialog(info, undefined, true);
  524.   },
  525.  
  526.   /**
  527.    * This is here for compatibility reasons, use ShowBookmarkDialog instead.
  528.    */
  529.   showItemProperties: function PUIU_showItemProperties(aItemId, aType, aReadOnly) {
  530.     this._reportDeprecatedAddBookmarkMethod();
  531.  
  532.     var info = {
  533.       action: "edit",
  534.       type: aType,
  535.       itemId: aItemId,
  536.       readOnly: aReadOnly
  537.     };
  538.     return this.showBookmarkDialog(info);
  539.   },
  540.  
  541.   /**
  542.    * This is here for compatibility reasons, use ShowBookmarkDialog instead.
  543.    */
  544.   showAddFolderUI:
  545.   function PUIU_showAddFolderUI(aTitle, aDefaultInsertionPoint, aShowPicker) {
  546.     this._reportDeprecatedAddBookmarkMethod();
  547.  
  548.     var info = {
  549.       action: "add",
  550.       type: "folder",
  551.       hiddenRows: []
  552.     };
  553.  
  554.     // allow default empty title
  555.     if (typeof(aTitle) == "string")
  556.       info.title = aTitle;
  557.  
  558.     if (aDefaultInsertionPoint) {
  559.       info.defaultInsertionPoint = aDefaultInsertionPoint;
  560.       if (!aShowPicker)
  561.         info.hiddenRows.push("folderPicker");
  562.     }
  563.     return this.showBookmarkDialog(info);
  564.   },
  565.  
  566.  
  567.   /**
  568.    * Shows the bookmark dialog corresponding to the specified info.
  569.    *
  570.    * @param aInfo
  571.    *        Describes the item to be edited/added in the dialog.
  572.    *        See documentation at the top of bookmarkProperties.js
  573.    * @param aWindow
  574.    *        Owner window for the new dialog.
  575.    * @param aMinimalUI [optional]
  576.    *        Whether to open the dialog in "minimal ui" mode. Do not pass this
  577.    *        for new callers.  It'll be removed in a future release.
  578.    *
  579.    * @see documentation at the top of bookmarkProperties.js
  580.    * @return true if any transaction has been performed, false otherwise.
  581.    */
  582.   showBookmarkDialog:
  583.   function PUIU_showBookmarkDialog(aInfo, aParentWindow, aMinimalUI) {
  584.     if (!aParentWindow) {
  585.       aParentWindow = this._getWindow(null);
  586.     }
  587.  
  588.     // Preserve size attributes differently based on the fact the dialog has
  589.     // a folder picker or not.
  590.     let minimalUI = "hiddenRows" in aInfo &&
  591.                     aInfo.hiddenRows.indexOf("folderPicker") != -1;
  592.     let dialogURL = aMinimalUI ?
  593.                     "chrome://browser/content/places/bookmarkProperties2.xul" :
  594.                     "chrome://browser/content/places/bookmarkProperties.xul";
  595.  
  596.     let features =
  597.       "centerscreen,chrome,modal,resizable=" + (aMinimalUI ? "yes" : "no");
  598.  
  599.     aParentWindow.openDialog(dialogURL, "",  features, aInfo);
  600.     return ("performed" in aInfo && aInfo.performed);
  601.   },
  602.  
  603.   _getTopBrowserWin: function PUIU__getTopBrowserWin() {
  604.     return Services.wm.getMostRecentWindow("navigator:browser");
  605.   },
  606.  
  607.   /**
  608.    * Returns the closet ancestor places view for the given DOM node
  609.    * @param aNode
  610.    *        a DOM node
  611.    * @return the closet ancestor places view if exists, null otherwsie.
  612.    */
  613.   getViewForNode: function PUIU_getViewForNode(aNode) {
  614.     let node = aNode;
  615.  
  616.     // The view for a <menu> of which its associated menupopup is a places
  617.     // view, is the menupopup.
  618.     if (node.localName == "menu" && !node._placesNode &&
  619.         node.lastChild._placesView)
  620.       return node.lastChild._placesView;
  621.  
  622.     while (node instanceof Ci.nsIDOMElement) {
  623.       if (node._placesView)
  624.         return node._placesView;
  625.       if (node.localName == "tree" && node.getAttribute("type") == "places")
  626.         return node;
  627.  
  628.       node = node.parentNode;
  629.     }
  630.  
  631.     return null;
  632.   },
  633.  
  634.   /**
  635.    * By calling this before visiting an URL, the visit will be associated to a
  636.    * TRANSITION_TYPED transition (if there is no a referrer).
  637.    * This is used when visiting pages from the history menu, history sidebar,
  638.    * url bar, url autocomplete results, and history searches from the places
  639.    * organizer.  If this is not called visits will be marked as
  640.    * TRANSITION_LINK.
  641.    */
  642.   markPageAsTyped: function PUIU_markPageAsTyped(aURL) {
  643.     PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory)
  644.                .markPageAsTyped(this.createFixedURI(aURL));
  645.   },
  646.  
  647.   /**
  648.    * By calling this before visiting an URL, the visit will be associated to a
  649.    * TRANSITION_BOOKMARK transition.
  650.    * This is used when visiting pages from the bookmarks menu,
  651.    * personal toolbar, and bookmarks from within the places organizer.
  652.    * If this is not called visits will be marked as TRANSITION_LINK.
  653.    */
  654.   markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) {
  655.     PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL));
  656.   },
  657.  
  658.   /**
  659.    * By calling this before visiting an URL, any visit in frames will be
  660.    * associated to a TRANSITION_FRAMED_LINK transition.
  661.    * This is actually used to distinguish user-initiated visits in frames
  662.    * so automatic visits can be correctly ignored.
  663.    */
  664.   markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) {
  665.     PlacesUtils.history.QueryInterface(Ci.nsIBrowserHistory)
  666.                .markPageAsFollowedLink(this.createFixedURI(aURL));
  667.   },
  668.  
  669.   /**
  670.    * Allows opening of javascript/data URI only if the given node is
  671.    * bookmarked (see bug 224521).
  672.    * @param aURINode
  673.    *        a URI node
  674.    * @param aWindow
  675.    *        a window on which a potential error alert is shown on.
  676.    * @return true if it's safe to open the node in the browser, false otherwise.
  677.    *
  678.    */
  679.   checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) {
  680.     if (PlacesUtils.nodeIsBookmark(aURINode))
  681.       return true;
  682.  
  683.     var uri = PlacesUtils._uri(aURINode.uri);
  684.     if (uri.schemeIs("javascript") || uri.schemeIs("data")) {
  685.       const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
  686.       var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
  687.                            getService(Ci.nsIStringBundleService).
  688.                            createBundle(BRANDING_BUNDLE_URI).
  689.                            GetStringFromName("brandShortName");
  690.  
  691.       var errorStr = this.getString("load-js-data-url-error");
  692.       Services.prompt.alert(aWindow, brandShortName, errorStr);
  693.       return false;
  694.     }
  695.     return true;
  696.   },
  697.  
  698.   /**
  699.    * Get the description associated with a document, as specified in a <META>
  700.    * element.
  701.    * @param   doc
  702.    *          A DOM Document to get a description for
  703.    * @returns A description string if a META element was discovered with a
  704.    *          "description" or "httpequiv" attribute, empty string otherwise.
  705.    */
  706.   getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) {
  707.     var metaElements = doc.getElementsByTagName("META");
  708.     for (var i = 0; i < metaElements.length; ++i) {
  709.       if (metaElements[i].name.toLowerCase() == "description" ||
  710.           metaElements[i].httpEquiv.toLowerCase() == "description") {
  711.         return metaElements[i].content;
  712.       }
  713.     }
  714.     return "";
  715.   },
  716.  
  717.   /**
  718.    * Retrieve the description of an item
  719.    * @param aItemId
  720.    *        item identifier
  721.    * @returns the description of the given item, or an empty string if it is
  722.    * not set.
  723.    */
  724.   getItemDescription: function PUIU_getItemDescription(aItemId) {
  725.     if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO))
  726.       return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO);
  727.     return "";
  728.   },
  729.  
  730.   /**
  731.    * Gives the user a chance to cancel loading lots of tabs at once
  732.    */
  733.   _confirmOpenInTabs:
  734.   function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) {
  735.     const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
  736.     var reallyOpen = true;
  737.  
  738.     if (Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) {
  739.       if (numTabsToOpen >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
  740.         // default to true: if it were false, we wouldn't get this far
  741.         var warnOnOpen = { value: true };
  742.  
  743.         var messageKey = "tabs.openWarningMultipleBranded";
  744.         var openKey = "tabs.openButtonMultiple";
  745.         const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
  746.         var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
  747.                              getService(Ci.nsIStringBundleService).
  748.                              createBundle(BRANDING_BUNDLE_URI).
  749.                              GetStringFromName("brandShortName");
  750.  
  751.         var buttonPressed = Services.prompt.confirmEx(
  752.           aWindow,
  753.           this.getString("tabs.openWarningTitle"),
  754.           this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
  755.           (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
  756.             (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
  757.           this.getString(openKey), null, null,
  758.           this.getFormattedString("tabs.openWarningPromptMeBranded",
  759.                                   [brandShortName]),
  760.           warnOnOpen
  761.         );
  762.  
  763.         reallyOpen = (buttonPressed == 0);
  764.         // don't set the pref unless they press OK and it's false
  765.         if (reallyOpen && !warnOnOpen.value)
  766.           Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false);
  767.       }
  768.     }
  769.  
  770.     return reallyOpen;
  771.   },
  772.  
  773.   /** aItemsToOpen needs to be an array of objects of the form:
  774.     * {uri: string, isBookmark: boolean}
  775.     */
  776.   _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) {
  777.     if (!aItemsToOpen.length)
  778.       return;
  779.  
  780.     var urls = [];
  781.     for (var i = 0; i < aItemsToOpen.length; i++) {
  782.       var item = aItemsToOpen[i];
  783.       if (item.isBookmark)
  784.         this.markPageAsFollowedBookmark(item.uri);
  785.       else
  786.         this.markPageAsTyped(item.uri);
  787.  
  788.       urls.push(item.uri);
  789.     }
  790.  
  791.     // Prefer the caller window if it's a browser window, otherwise use
  792.     // the top browser window.
  793.     var browserWindow = null;
  794.     browserWindow =
  795.       aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ?
  796.       aWindow : this._getTopBrowserWin();
  797.  
  798.     // whereToOpenLink doesn't return "window" when there's no browser window
  799.     // open (Bug 630255).
  800.     var where = browserWindow ?
  801.                 browserWindow.whereToOpenLink(aEvent, false, true) : "window";
  802.     if (where == "window") {
  803.       // There is no browser window open, thus open a new one.
  804.       var uriList = PlacesUtils.toISupportsString(urls.join("|"));
  805.       var args = Cc["@mozilla.org/supports-array;1"].
  806.                   createInstance(Ci.nsISupportsArray);
  807.       args.AppendElement(uriList);      
  808.       browserWindow = Services.ww.openWindow(aWindow,
  809.                                              "chrome://browser/content/browser.xul",
  810.                                              null, "chrome,dialog=no,all", args);
  811.       return;
  812.     }
  813.  
  814.     var loadInBackground = where == "tabshifted" ? true : false;
  815.     // For consistency, we want all the bookmarks to open in new tabs, instead
  816.     // of having one of them replace the currently focused tab.  Hence we call
  817.     // loadTabs with aReplace set to false.
  818.     browserWindow.gBrowser.loadTabs(urls, loadInBackground, false);
  819.   },
  820.  
  821.   /**
  822.    * Helper method for methods which are forced to take a view/window
  823.    * parameter as an optional parameter.  It will be removed post Fx4.
  824.    */
  825.   _getWindow: function PUIU__getWindow(aView) {
  826.     if (aView) {
  827.       // Pratically, this is the case for places trees.
  828.       if (aView instanceof Components.interfaces.nsIDOMNode)
  829.         return aView.ownerDocument.defaultView;
  830.  
  831.       return Cu.getGlobalForObject(aView);
  832.     }
  833.  
  834.     let caller = arguments.callee.caller;
  835.  
  836.     // If a view wasn't expected, the method should have got a window.
  837.     if (aView === null) {
  838.       Components.utils.reportError("The api has changed. A window should be " +
  839.                                    "passed to " + caller.name + ".  Not " +
  840.                                    "passing a window will throw in a future " +
  841.                                    "release.");
  842.     }
  843.     else {
  844.       Components.utils.reportError("The api has changed. A places view " +
  845.                                    "should be passed to " + caller.name + ". " +
  846.                                    "Not passing a view will throw in a future " +
  847.                                    "release.");
  848.     }
  849.  
  850.     // This could certainly break in some edge cases (like bug 562998), but
  851.     // that's the best we should do for those extreme backwards-compatibility cases.
  852.     let topBrowserWin = this._getTopBrowserWin();
  853.     return topBrowserWin ? topBrowserWin : focusManager.focusedWindow;
  854.   },
  855.  
  856.   openContainerNodeInTabs:
  857.   function PUIU_openContainerInTabs(aNode, aEvent, aView) {
  858.     let window = this._getWindow(aView);
  859.  
  860.     let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
  861.     if (!this._confirmOpenInTabs(urlsToOpen.length, window))
  862.       return;
  863.  
  864.     this._openTabset(urlsToOpen, aEvent, window);
  865.   },
  866.  
  867.   openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) {
  868.     let window = this._getWindow(aView);
  869.  
  870.     let urlsToOpen = [];
  871.     for (var i=0; i < aNodes.length; i++) {
  872.       // Skip over separators and folders.
  873.       if (PlacesUtils.nodeIsURI(aNodes[i]))
  874.         urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
  875.     }
  876.     this._openTabset(urlsToOpen, aEvent, window);
  877.   },
  878.  
  879.   /**
  880.    * Loads the node's URL in the appropriate tab or window or as a web
  881.    * panel given the user's preference specified by modifier keys tracked by a
  882.    * DOM mouse/key event.
  883.    * @param   aNode
  884.    *          An uri result node.
  885.    * @param   aEvent
  886.    *          The DOM mouse/key event with modifier keys set that track the
  887.    *          user's preferred destination window or tab.
  888.    * @param   aView
  889.    *          The controller associated with aNode.
  890.    */
  891.   openNodeWithEvent:
  892.   function PUIU_openNodeWithEvent(aNode, aEvent, aView) {
  893.     let window = this._getWindow(aView);
  894.     this._openNodeIn(aNode, window.whereToOpenLink(aEvent), window);
  895.   },
  896.  
  897.   /**
  898.    * Loads the node's URL in the appropriate tab or window or as a
  899.    * web panel.
  900.    * see also openUILinkIn
  901.    */
  902.   openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView) {
  903.     let window = this._getWindow(aView);
  904.     this._openNodeIn(aNode, aWhere, window);
  905.   },
  906.  
  907.   _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow) {
  908.     if (aNode && PlacesUtils.nodeIsURI(aNode) &&
  909.         this.checkURLSecurity(aNode, aWindow)) {
  910.       let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
  911.  
  912.       if (isBookmark)
  913.         this.markPageAsFollowedBookmark(aNode.uri);
  914.       else
  915.         this.markPageAsTyped(aNode.uri);
  916.  
  917.       // Check whether the node is a bookmark which should be opened as
  918.       // a web panel
  919.       if (aWhere == "current" && isBookmark) {
  920.         if (PlacesUtils.annotations
  921.                        .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
  922.           let browserWin = this._getTopBrowserWin();
  923.           if (browserWin) {
  924.             browserWin.openWebPanel(aNode.title, aNode.uri);
  925.             return;
  926.           }
  927.         }
  928.       }
  929.       aWindow.openUILinkIn(aNode.uri, aWhere);
  930.     }
  931.   },
  932.  
  933.   /**
  934.    * Helper for guessing scheme from an url string.
  935.    * Used to avoid nsIURI overhead in frequently called UI functions.
  936.    *
  937.    * @param aUrlString the url to guess the scheme from.
  938.    *
  939.    * @return guessed scheme for this url string.
  940.    *
  941.    * @note this is not supposed be perfect, so use it only for UI purposes.
  942.    */
  943.   guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) {
  944.     return aUrlString.substr(0, aUrlString.indexOf(":"));
  945.   },
  946.  
  947.   getBestTitle: function PUIU_getBestTitle(aNode) {
  948.     var title;
  949.     if (!aNode.title && PlacesUtils.uriTypes.indexOf(aNode.type) != -1) {
  950.       // if node title is empty, try to set the label using host and filename
  951.       // PlacesUtils._uri() will throw if aNode.uri is not a valid URI
  952.       try {
  953.         var uri = PlacesUtils._uri(aNode.uri);
  954.         var host = uri.host;
  955.         var fileName = uri.QueryInterface(Ci.nsIURL).fileName;
  956.         // if fileName is empty, use path to distinguish labels
  957.         title = host + (fileName ?
  958.                         (host ? "/" + this.ellipsis + "/" : "") + fileName :
  959.                         uri.path);
  960.       }
  961.       catch (e) {
  962.         // Use (no title) for non-standard URIs (data:, javascript:, ...)
  963.         title = "";
  964.       }
  965.     }
  966.     else
  967.       title = aNode.title;
  968.  
  969.     return title || this.getString("noTitle");
  970.   },
  971.  
  972.   get leftPaneQueries() {
  973.     // build the map
  974.     this.leftPaneFolderId;
  975.     return this.leftPaneQueries;
  976.   },
  977.  
  978.   // Get the folder id for the organizer left-pane folder.
  979.   get leftPaneFolderId() {
  980.     let leftPaneRoot = -1;
  981.     let allBookmarksId;
  982.  
  983.     // Shortcuts to services.
  984.     let bs = PlacesUtils.bookmarks;
  985.     let as = PlacesUtils.annotations;
  986.  
  987.     // This is the list of the left pane queries.
  988.     let queries = {
  989.       "PlacesRoot": { title: "" },
  990.       "History": { title: this.getString("OrganizerQueryHistory") },
  991.       "Downloads": { title: this.getString("OrganizerQueryDownloads") },
  992.       "Tags": { title: this.getString("OrganizerQueryTags") },
  993.       "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
  994.       "BookmarksToolbar":
  995.         { title: null,
  996.           concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"),
  997.           concreteId: PlacesUtils.toolbarFolderId },
  998.       "BookmarksMenu":
  999.         { title: null,
  1000.           concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"),
  1001.           concreteId: PlacesUtils.bookmarksMenuFolderId },
  1002.       "UnfiledBookmarks":
  1003.         { title: null,
  1004.           concreteTitle: PlacesUtils.getString("UnsortedBookmarksFolderTitle"),
  1005.           concreteId: PlacesUtils.unfiledBookmarksFolderId },
  1006.     };
  1007.     // All queries but PlacesRoot.
  1008.     const EXPECTED_QUERY_COUNT = 7;
  1009.  
  1010.     // Removes an item and associated annotations, ignoring eventual errors.
  1011.     function safeRemoveItem(aItemId) {
  1012.       try {
  1013.         if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) &&
  1014.             !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) {
  1015.           // Some extension annotated their roots with our query annotation,
  1016.           // so we should not delete them.
  1017.           return;
  1018.         }
  1019.         // removeItemAnnotation does not check if item exists, nor the anno,
  1020.         // so this is safe to do.
  1021.         as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
  1022.         as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO);
  1023.         // This will throw if the annotation is an orphan.
  1024.         bs.removeItem(aItemId);
  1025.       }
  1026.       catch(e) { /* orphan anno */ }
  1027.     }
  1028.  
  1029.     // Returns true if item really exists, false otherwise.
  1030.     function itemExists(aItemId) {
  1031.       try {
  1032.         bs.getItemIndex(aItemId);
  1033.         return true;
  1034.       }
  1035.       catch(e) {
  1036.         return false;
  1037.       }
  1038.     }
  1039.  
  1040.     // Get all items marked as being the left pane folder.
  1041.     let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO);
  1042.     if (items.length > 1) {
  1043.       // Something went wrong, we cannot have more than one left pane folder,
  1044.       // remove all left pane folders and continue.  We will create a new one.
  1045.       items.forEach(safeRemoveItem);
  1046.     }
  1047.     else if (items.length == 1 && items[0] != -1) {
  1048.       leftPaneRoot = items[0];
  1049.       // Check that organizer left pane root is valid.
  1050.       let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO);
  1051.       if (version != this.ORGANIZER_LEFTPANE_VERSION ||
  1052.           !itemExists(leftPaneRoot)) {
  1053.         // Invalid root, we must rebuild the left pane.
  1054.         safeRemoveItem(leftPaneRoot);
  1055.         leftPaneRoot = -1;
  1056.       }
  1057.     }
  1058.  
  1059.     if (leftPaneRoot != -1) {
  1060.       // A valid left pane folder has been found.
  1061.       // Build the leftPaneQueries Map.  This is used to quickly access them,
  1062.       // associating a mnemonic name to the real item ids.
  1063.       delete this.leftPaneQueries;
  1064.       this.leftPaneQueries = {};
  1065.  
  1066.       let items = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO);
  1067.       // While looping through queries we will also check for their validity.
  1068.       let queriesCount = 0;
  1069.       for (let i = 0; i < items.length; i++) {
  1070.         let queryName = as.getItemAnnotation(items[i], this.ORGANIZER_QUERY_ANNO);
  1071.  
  1072.         // Some extension did use our annotation to decorate their items
  1073.         // with icons, so we should check only our elements, to avoid dataloss.
  1074.         if (!(queryName in queries))
  1075.           continue;
  1076.  
  1077.         let query = queries[queryName];
  1078.         query.itemId = items[i];
  1079.  
  1080.         if (!itemExists(query.itemId)) {
  1081.           // Orphan annotation, bail out and create a new left pane root.
  1082.           break;
  1083.         }
  1084.  
  1085.         // Check that all queries have valid parents.
  1086.         let parentId = bs.getFolderIdForItem(query.itemId);
  1087.         if (items.indexOf(parentId) == -1 && parentId != leftPaneRoot) {
  1088.           // The parent is not part of the left pane, bail out and create a new
  1089.           // left pane root.
  1090.           break;
  1091.         }
  1092.  
  1093.         // Titles could have been corrupted or the user could have changed his
  1094.         // locale.  Check title and eventually fix it.
  1095.         if (bs.getItemTitle(query.itemId) != query.title)
  1096.           bs.setItemTitle(query.itemId, query.title);
  1097.         if ("concreteId" in query) {
  1098.           if (bs.getItemTitle(query.concreteId) != query.concreteTitle)
  1099.             bs.setItemTitle(query.concreteId, query.concreteTitle);
  1100.         }
  1101.  
  1102.         // Add the query to our cache.
  1103.         this.leftPaneQueries[queryName] = query.itemId;
  1104.         queriesCount++;
  1105.       }
  1106.  
  1107.       if (queriesCount != EXPECTED_QUERY_COUNT) {
  1108.         // Queries number is wrong, so the left pane must be corrupt.
  1109.         // Note: we can't just remove the leftPaneRoot, because some query could
  1110.         // have a bad parent, so we have to remove all items one by one.
  1111.         items.forEach(safeRemoveItem);
  1112.         safeRemoveItem(leftPaneRoot);
  1113.       }
  1114.       else {
  1115.         // Everything is fine, return the current left pane folder.
  1116.         delete this.leftPaneFolderId;
  1117.         return this.leftPaneFolderId = leftPaneRoot;
  1118.       }
  1119.     }
  1120.  
  1121.     // Create a new left pane folder.
  1122.     var callback = {
  1123.       // Helper to create an organizer special query.
  1124.       create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) {
  1125.         let itemId = bs.insertBookmark(aParentId,
  1126.                                        PlacesUtils._uri(aQueryUrl),
  1127.                                        bs.DEFAULT_INDEX,
  1128.                                        queries[aQueryName].title);
  1129.         // Mark as special organizer query.
  1130.         as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName,
  1131.                              0, as.EXPIRE_NEVER);
  1132.         // We should never backup this, since it changes between profiles.
  1133.         as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
  1134.                              0, as.EXPIRE_NEVER);
  1135.         // Add to the queries map.
  1136.         PlacesUIUtils.leftPaneQueries[aQueryName] = itemId;
  1137.         return itemId;
  1138.       },
  1139.  
  1140.       // Helper to create an organizer special folder.
  1141.       create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
  1142.               // Left Pane Root Folder.
  1143.         let folderId = bs.createFolder(aParentId,
  1144.                                        queries[aFolderName].title,
  1145.                                        bs.DEFAULT_INDEX);
  1146.         // We should never backup this, since it changes between profiles.
  1147.         as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
  1148.                              0, as.EXPIRE_NEVER);
  1149.         // Disallow manipulating this folder within the organizer UI.
  1150.         bs.setFolderReadonly(folderId, true);
  1151.  
  1152.         if (aIsRoot) {
  1153.           // Mark as special left pane root.
  1154.           as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
  1155.                                PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
  1156.                                0, as.EXPIRE_NEVER);
  1157.         }
  1158.         else {
  1159.           // Mark as special organizer folder.
  1160.           as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName,
  1161.                            0, as.EXPIRE_NEVER);
  1162.           PlacesUIUtils.leftPaneQueries[aFolderName] = folderId;
  1163.         }
  1164.         return folderId;
  1165.       },
  1166.  
  1167.       runBatched: function CB_runBatched(aUserData) {
  1168.         delete PlacesUIUtils.leftPaneQueries;
  1169.         PlacesUIUtils.leftPaneQueries = { };
  1170.  
  1171.         // Left Pane Root Folder.
  1172.         leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true);
  1173.  
  1174.         // History Query.
  1175.         this.create_query("History", leftPaneRoot,
  1176.                           "place:type=" +
  1177.                           Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
  1178.                           "&sort=" +
  1179.                           Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
  1180.  
  1181.         // Downloads.
  1182.         this.create_query("Downloads", leftPaneRoot,
  1183.                           "place:transition=" +
  1184.                           Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
  1185.                           "&sort=" +
  1186.                           Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
  1187.  
  1188.         // Tags Query.
  1189.         this.create_query("Tags", leftPaneRoot,
  1190.                           "place:type=" +
  1191.                           Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
  1192.                           "&sort=" +
  1193.                           Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
  1194.  
  1195.         // All Bookmarks Folder.
  1196.         allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false);
  1197.  
  1198.         // All Bookmarks->Bookmarks Toolbar Query.
  1199.         this.create_query("BookmarksToolbar", allBookmarksId,
  1200.                           "place:folder=TOOLBAR");
  1201.  
  1202.         // All Bookmarks->Bookmarks Menu Query.
  1203.         this.create_query("BookmarksMenu", allBookmarksId,
  1204.                           "place:folder=BOOKMARKS_MENU");
  1205.  
  1206.         // All Bookmarks->Unfiled Bookmarks Query.
  1207.         this.create_query("UnfiledBookmarks", allBookmarksId,
  1208.                           "place:folder=UNFILED_BOOKMARKS");
  1209.       }
  1210.     };
  1211.     bs.runInBatchMode(callback, null);
  1212.  
  1213.     delete this.leftPaneFolderId;
  1214.     return this.leftPaneFolderId = leftPaneRoot;
  1215.   },
  1216.  
  1217.   /**
  1218.    * Get the folder id for the organizer left-pane folder.
  1219.    */
  1220.   get allBookmarksFolderId() {
  1221.     // ensure the left-pane root is initialized;
  1222.     this.leftPaneFolderId;
  1223.     delete this.allBookmarksFolderId;
  1224.     return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"];
  1225.   },
  1226.  
  1227.   /**
  1228.    * If an item is a left-pane query, returns the name of the query
  1229.    * or an empty string if not.
  1230.    *
  1231.    * @param aItemId id of a container
  1232.    * @returns the name of the query, or empty string if not a left-pane query
  1233.    */
  1234.   getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) {
  1235.     var queryName = "";
  1236.     // If the let pane hasn't been built, use the annotation service
  1237.     // directly, to avoid building the left pane too early.
  1238.     if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) {
  1239.       try {
  1240.         queryName = PlacesUtils.annotations.
  1241.                                 getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO);
  1242.       }
  1243.       catch (ex) {
  1244.         // doesn't have the annotation
  1245.         queryName = "";
  1246.       }
  1247.     }
  1248.     else {
  1249.       // If the left pane has already been built, use the name->id map
  1250.       // cached in PlacesUIUtils.
  1251.       for (let [name, id] in Iterator(this.leftPaneQueries)) {
  1252.         if (aItemId == id)
  1253.           queryName = name;
  1254.       }
  1255.     }
  1256.     return queryName;
  1257.   }
  1258. };
  1259.  
  1260. XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF",
  1261.                                    "@mozilla.org/rdf/rdf-service;1",
  1262.                                    "nsIRDFService");
  1263.  
  1264. XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() {
  1265.   return PlacesUIUtils.RDF.GetDataSource("rdf:local-store");
  1266. });
  1267.  
  1268. XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
  1269.   return Services.prefs.getComplexValue("intl.ellipsis",
  1270.                                         Ci.nsIPrefLocalizedString).data;
  1271. });
  1272.  
  1273. XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "privateBrowsing",
  1274.                                    "@mozilla.org/privatebrowsing;1",
  1275.                                    "nsIPrivateBrowsingService");
  1276.  
  1277. XPCOMUtils.defineLazyServiceGetter(this, "URIFixup",
  1278.                                    "@mozilla.org/docshell/urifixup;1",
  1279.                                    "nsIURIFixup");
  1280.  
  1281. XPCOMUtils.defineLazyGetter(this, "bundle", function() {
  1282.   const PLACES_STRING_BUNDLE_URI =
  1283.     "chrome://browser/locale/places/places.properties";
  1284.   return Cc["@mozilla.org/intl/stringbundle;1"].
  1285.          getService(Ci.nsIStringBundleService).
  1286.          createBundle(PLACES_STRING_BUNDLE_URI);
  1287. });
  1288.  
  1289. XPCOMUtils.defineLazyServiceGetter(this, "focusManager",
  1290.                                    "@mozilla.org/focus-manager;1",
  1291.                                    "nsIFocusManager");
  1292.  
  1293. /**
  1294.  * This is a compatibility shim for old PUIU.ptm users.
  1295.  *
  1296.  * If you're looking for transactions and writing new code using them, directly
  1297.  * use the transactions objects exported by the PlacesUtils.jsm module.
  1298.  *
  1299.  * This object will be removed once enough users are converted to the new API.
  1300.  */
  1301. XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() {
  1302.   // Ensure PlacesUtils is imported in scope.
  1303.   PlacesUtils;
  1304.  
  1305.   return {
  1306.     aggregateTransactions: function(aName, aTransactions)
  1307.       new PlacesAggregatedTransaction(aName, aTransactions),
  1308.  
  1309.     createFolder: function(aName, aContainer, aIndex, aAnnotations,
  1310.                            aChildItemsTransactions)
  1311.       new PlacesCreateFolderTransaction(aName, aContainer, aIndex, aAnnotations,
  1312.                                         aChildItemsTransactions),
  1313.  
  1314.     createItem: function(aURI, aContainer, aIndex, aTitle, aKeyword,
  1315.                          aAnnotations, aChildTransactions)
  1316.       new PlacesCreateBookmarkTransaction(aURI, aContainer, aIndex, aTitle,
  1317.                                           aKeyword, aAnnotations,
  1318.                                           aChildTransactions),
  1319.  
  1320.     createSeparator: function(aContainer, aIndex)
  1321.       new PlacesCreateSeparatorTransaction(aContainer, aIndex),
  1322.  
  1323.     createLivemark: function(aFeedURI, aSiteURI, aName, aContainer, aIndex,
  1324.                              aAnnotations)
  1325.       new PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, aContainer,
  1326.                                           aIndex, aAnnotations),
  1327.  
  1328.     moveItem: function(aItemId, aNewContainer, aNewIndex)
  1329.       new PlacesMoveItemTransaction(aItemId, aNewContainer, aNewIndex),
  1330.  
  1331.     removeItem: function(aItemId)
  1332.       new PlacesRemoveItemTransaction(aItemId),
  1333.  
  1334.     editItemTitle: function(aItemId, aNewTitle)
  1335.       new PlacesEditItemTitleTransaction(aItemId, aNewTitle),
  1336.  
  1337.     editBookmarkURI: function(aItemId, aNewURI)
  1338.       new PlacesEditBookmarkURITransaction(aItemId, aNewURI),
  1339.  
  1340.     setItemAnnotation: function(aItemId, aAnnotationObject)
  1341.       new PlacesSetItemAnnotationTransaction(aItemId, aAnnotationObject),
  1342.  
  1343.     setPageAnnotation: function(aURI, aAnnotationObject)
  1344.       new PlacesSetPageAnnotationTransaction(aURI, aAnnotationObject),
  1345.  
  1346.     editBookmarkKeyword: function(aItemId, aNewKeyword)
  1347.       new PlacesEditBookmarkKeywordTransaction(aItemId, aNewKeyword),
  1348.  
  1349.     editBookmarkPostData: function(aItemId, aPostData)
  1350.       new PlacesEditBookmarkPostDataTransaction(aItemId, aPostData),
  1351.  
  1352.     editLivemarkSiteURI: function(aLivemarkId, aSiteURI)
  1353.       new PlacesEditLivemarkSiteURITransaction(aLivemarkId, aSiteURI),
  1354.  
  1355.     editLivemarkFeedURI: function(aLivemarkId, aFeedURI)
  1356.       new PlacesEditLivemarkFeedURITransaction(aLivemarkId, aFeedURI),
  1357.  
  1358.     editItemDateAdded: function(aItemId, aNewDateAdded)
  1359.       new PlacesEditItemDateAddedTransaction(aItemId, aNewDateAdded),
  1360.  
  1361.     editItemLastModified: function(aItemId, aNewLastModified)
  1362.       new PlacesEditItemLastModifiedTransaction(aItemId, aNewLastModified),
  1363.  
  1364.     sortFolderByName: function(aFolderId)
  1365.       new PlacesSortFolderByNameTransaction(aFolderId),
  1366.  
  1367.     tagURI: function(aURI, aTags)
  1368.       new PlacesTagURITransaction(aURI, aTags),
  1369.  
  1370.     untagURI: function(aURI, aTags)
  1371.       new PlacesUntagURITransaction(aURI, aTags),
  1372.  
  1373.     /**
  1374.      * Transaction for setting/unsetting Load-in-sidebar annotation.
  1375.      *
  1376.      * @param aBookmarkId
  1377.      *        id of the bookmark where to set Load-in-sidebar annotation.
  1378.      * @param aLoadInSidebar
  1379.      *        boolean value.
  1380.      * @returns nsITransaction object.
  1381.      */
  1382.     setLoadInSidebar: function(aItemId, aLoadInSidebar)
  1383.     {
  1384.       let annoObj = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
  1385.                       type: Ci.nsIAnnotationService.TYPE_INT32,
  1386.                       flags: 0,
  1387.                       value: aLoadInSidebar,
  1388.                       expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
  1389.       return new PlacesSetItemAnnotationTransaction(aItemId, annoObj);
  1390.     },
  1391.  
  1392.    /**
  1393.     * Transaction for editing the description of a bookmark or a folder.
  1394.     *
  1395.     * @param aItemId
  1396.     *        id of the item to edit.
  1397.     * @param aDescription
  1398.     *        new description.
  1399.     * @returns nsITransaction object.
  1400.     */
  1401.     editItemDescription: function(aItemId, aDescription)
  1402.     {
  1403.       let annoObj = { name: PlacesUIUtils.DESCRIPTION_ANNO,
  1404.                       type: Ci.nsIAnnotationService.TYPE_STRING,
  1405.                       flags: 0,
  1406.                       value: aDescription,
  1407.                       expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
  1408.       return new PlacesSetItemAnnotationTransaction(aItemId, annoObj);
  1409.     },
  1410.  
  1411.     ////////////////////////////////////////////////////////////////////////////
  1412.     //// nsITransactionManager forwarders.
  1413.  
  1414.     beginBatch: function()
  1415.       PlacesUtils.transactionManager.beginBatch(),
  1416.  
  1417.     endBatch: function()
  1418.       PlacesUtils.transactionManager.endBatch(),
  1419.  
  1420.     doTransaction: function(txn)
  1421.       PlacesUtils.transactionManager.doTransaction(txn),
  1422.  
  1423.     undoTransaction: function()
  1424.       PlacesUtils.transactionManager.undoTransaction(),
  1425.  
  1426.     redoTransaction: function()
  1427.       PlacesUtils.transactionManager.redoTransaction(),
  1428.  
  1429.     get numberOfUndoItems()
  1430.       PlacesUtils.transactionManager.numberOfUndoItems,
  1431.     get numberOfRedoItems()
  1432.       PlacesUtils.transactionManager.numberOfRedoItems,
  1433.     get maxTransactionCount()
  1434.       PlacesUtils.transactionManager.maxTransactionCount,
  1435.     set maxTransactionCount(val)
  1436.       PlacesUtils.transactionManager.maxTransactionCount = val,
  1437.  
  1438.     clear: function()
  1439.       PlacesUtils.transactionManager.clear(),
  1440.  
  1441.     peekUndoStack: function()
  1442.       PlacesUtils.transactionManager.peekUndoStack(),
  1443.  
  1444.     peekRedoStack: function()
  1445.       PlacesUtils.transactionManager.peekRedoStack(),
  1446.  
  1447.     getUndoStack: function()
  1448.       PlacesUtils.transactionManager.getUndoStack(),
  1449.  
  1450.     getRedoStack: function()
  1451.       PlacesUtils.transactionManager.getRedoStack(),
  1452.  
  1453.     AddListener: function(aListener)
  1454.       PlacesUtils.transactionManager.AddListener(aListener),
  1455.  
  1456.     RemoveListener: function(aListener)
  1457.       PlacesUtils.transactionManager.RemoveListener(aListener)
  1458.   }
  1459. });
  1460.